{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 튜토리얼: Private Deep Learning의 기초 도구\n",
    "\n",
    "사생활 보호와 탈중앙화된 딥러닝을 위한 PySyft의 튜토리얼에 오신 것을 환영합니다. 이 튜토리얼을 통해 기밀이거나 사적인 데이터 또는 모델에 대한 딥러닝을 한 기관에서 중앙화하지 않고 수행하는 데 필요한 새로운 도구와 기술을 익힐 수 있습니다. \n",
    "\n",
    "**범위:** 우리는 데이터를 어떻게 탈중앙화하고 암호화할 것인지에 대해서만 이야기하려는 것이 아니라 어떻게 하면 PySyft를 사용하여 데이터에 관한 전체 생태계를 탈중앙화시키는 데에 도움이 될 수 있을지에 대해 다룰 것입니다. 데이터가 저장되고 쿼리가 실행되는 데이터베이스를 포함해서 데이터로부터 정보를 추출하는데 사용되는 인공신경망까지 포함됩니다. PySyft에 대한 새로운 확장이 만들어짐에 따라 이 파이썬 노트북은 새로운 기능을 설명하는 튜토리얼로 확장될 것입니다.\n",
    "\n",
    "저자:\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n",
    "\n",
    "역자:\n",
    "- Wonsuk Kim - Github: [@wonderit](https://github.com/wonderit) \n",
    "\n",
    "## 개요:\n",
    "\n",
    "- 파트 1: Private Deep Learning의 기초 도구\n",
    "\n",
    "\n",
    "## 왜 이 튜토리얼을 진행해야 할까요?\n",
    "\n",
    "**1) 경쟁력있는 커리어** - 지난 20년 동안 디지털 혁명은 아날로그 프로세스가 디지털화 됨에 따라 점점 더 많은 양의 데이터에 접근할 수 있게 만들었습니다. 그러나 [GDPR](https://eugdpr.org/)과 같은 새로운 규제로 인해 기업은 개인 정보를 사용하는 방식에 대한 자유가 줄어들고, 더 중요한 것은 개인 정보를 분석하는 방식에 대한 압박을 받고 있습니다. **결론** : 데이터 과학자들은 \"옛날 방식의\" 도구를 사용하여 많은 양의 데이터에 액세스 할 수는 없지만 Private Deep Learning의 기법들을 익히고 나면 이 흐름에 앞설 수 있고 커리어에 있어서 경쟁 우위를 점할 수 있습니다.  \n",
    "\n",
    "**2) 새로운 사업 아이템** - 사회에는 딥러닝으로 해결할 수 있는 많은 문제가 있지만, 가장 중요한 문제들 중에 다수는 사람들의 아주 민감한 정보에 접근할 수 있어야 하기 때문에 탐구되지 못했습니다. (정신적 혹은 관계 문제가 있는 사람들을 돕기 위해 딥러닝을 사용하는 것을 생각해보세요!). 따라서 Private Deep Learning을 익하면 이전에 이러한 개인 정보 이슈 때문에 하지 못했던 새로운 스타트업의 가능성을 열어줄 것입니다. \n",
    "\n",
    "**3) 공동 선 추구** - 딥러닝은 실제 세계에서 다양한 문제를 해결하는 데 사용될 수 있지만 *개인 정보*에 대한 딥러닝은 사람들에 대한, *사람들을 위한* 딥러닝입니다. 소유하지 않은 데이터에 대해 딥러닝을 수행하는 방법을 배우는 것은 커리어나 사업 기회 이상을 의미합니다. 사람들의 삶에서 가장 개인적이고 중요한 문제를 해결할 수 있는 기회이고 이를 대규모로 확장할 수 있는 기회입니다. \n",
    "\n",
    "## 추가 크레딧을 받으려면 어떻게 해야하나요?\n",
    "\n",
    "- PySyft github에 Star버튼을 눌러주세요! - [https://github.com/OpenMined/PySyft](https://github.com/OpenMined/PySyft)\n",
    "- 이 파이썬 노트북을 가르치는 유투브 영상을 만드세요!\n",
    "\n",
    "\n",
    "... 자 ... 그럼 이제 시작합다!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 파트 -1: 필요한 선수 지식\n",
    "\n",
    "- PyTorch를 아십시오 - 모른다면 http://fast.ai 코스를 들으시고 다시 오세요\n",
    "- PySyft 논문을 읽으세요 https://arxiv.org/pdf/1811.04017.pdf! 이 논문을 읽으면 PySyft가 어떻게 구성되어 있는지 완전한 배경을 제공하여 이해하는 데 도움이 될 것입니다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 파트 0: 설치하기\n",
    "\n",
    "시작하려면 올바른 항목이 설치되어 있는지 확인해야 합니다. 그렇게 하려면 PySyft의 readme에 가셔서 설치 방법을 따르십시오. 대부분의 사람들에 해당하는 것으로 요약하자면.\n",
    "\n",
    "- Install Python 3.5 or higher\n",
    "- Install PyTorch 1.1\n",
    "- Clone PySyft (git clone https://github.com/OpenMined/PySyft.git)\n",
    "- cd PySyft\n",
    "- pip install -r pip-dep/requirements.txt\n",
    "- pip install -r pip-dep/requirements_udacity.txt\n",
    "- python setup.py install udacity\n",
    "- python setup.py test\n",
    "\n",
    "이 중 하나라도 작동하지 않는 경우 (또는 테스트가 실패한 경우) - 먼저 [README](https://github.com/OpenMined/PySyft.git)에서 설치 도움말을 확인한 다음 GitHub Issue를 만들거나 혹은 Slack에 #beginner 채널에 글을 남기세요! [slack.openmined.org](http://slack.openmined.org/)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Falling back to insecure randomness since the required custom op could not be found for the installed version of TensorFlow. Fix this by compiling custom ops. Missing file was '/opt/anaconda3/envs/pysyft/lib/python3.7/site-packages/tf_encrypted/operations/secure_random/secure_random_module_tf_1.15.0.so'\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /opt/anaconda3/envs/pysyft/lib/python3.7/site-packages/tf_encrypted/session.py:24: The name tf.Session is deprecated. Please use tf.compat.v1.Session instead.\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "tensor([1, 2, 3, 4, 5])"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 이 셀을 실행시켜서 설치가 잘 되었는지 확인해 보세요\n",
    "import sys\n",
    "\n",
    "import torch\n",
    "from torch.nn import Parameter\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "import syft as sy\n",
    "hook = sy.TorchHook(torch)\n",
    "\n",
    "torch.tensor([1,2,3,4,5])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이 셀이 실행된다면, 그럼 당신은 이제 달릴 준비가 된 것입니다! 자 이제 시작합시다!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 파트 1: 사적인, 탈중앙화된 데이터과학의 기초 도구\n",
    "\n",
    "아마 당신이 궁금하게 생각할 수 있는 첫 번째 질문은 어떻게 우리가 접근할 수 없는 데이터에 대해 모델을 훈련시키느냐에 대한 질문일 것입니다. \n",
    "\n",
    "음, 대답은 놀랍도록 간단합니다. 만약 당신이 PyTorch를 기존에 썼던 사람이라면 아마 아래처럼 torch.Tensor 객체를 사용하는 데에 익숙할 것입니다! "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([ 2,  4,  6,  8, 10])\n"
     ]
    }
   ],
   "source": [
    "x = torch.tensor([1,2,3,4,5])\n",
    "y = x + x\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "물론, 이 고급스러운 (그리고 강력한!) 텐서를 사용하는 것이 중요하지만 로컬 머신에 데이터가 있어야 합니다. 이것이 우리의 여행이 시작되는 곳입니다.  \n",
    "\n",
    "# 섹션 1.1 - Bob의 머신에 텐서를 보내기\n",
    "\n",
    "일반적으로 데이터를 보유한 머신에서 데이터 분석 / 딥러닝을 수행하지만 이제 우리는 **다른**머신에서 이러한 종류의 계산을 수행하려고 합니다. 보다 구체적으로 말하자면 우리는 더이상 데이터가 로컬 머신에 있다고 가정할 수 없습니다. \n",
    "\n",
    "따라서 Torch 텐서를 이용하는 대신에 **포인터**를 사용하여 텐서를 이용할 것입니다. 제가 무엇을 말하려고 하는지 보여드리겠습니다. 먼저 \"가상의\" 사람이 소유한 \"가상의\" 머신을 만들고 그 사람을 Bob이라고 합시다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [],
   "source": [
    "bob = sy.VirtualWorker(hook, id=\"bob\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Bob의 머신이 마치 화성처럼 다른 행성에 있다고 가정해 봅시다! 그러나 현재 머신이 비어 있습니다. Bob에게 데이터를 보내고 포인터에 대해 배울 수 있도록 몇 가지 데이터를 만들어 봅시다!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5])\n",
    "y = torch.tensor([1,1,1,1,1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "그리고 이제 우리의 텐서를 Bob에게 보냅시다!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [],
   "source": [
    "x_ptr = x.send(bob)\n",
    "y_ptr = y.send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:89072528493 -> bob:57522835638]"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "짜잔! 이제 Bob은 두 개의 텐서를 가지고 있습니다! 믿기지 않으신가요? 한 번 확인해 보세요!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{57522835638: tensor([1, 2, 3, 4, 5]), 51633454228: tensor([1, 1, 1, 1, 1])}"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [],
   "source": [
    "z = x_ptr + x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:69973865919 -> bob:39363602794]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{57522835638: tensor([1, 2, 3, 4, 5]),\n",
       " 51633454228: tensor([1, 1, 1, 1, 1]),\n",
       " 39363602794: tensor([ 2,  4,  6,  8, 10])}"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이제 무언가를 주목하십시오. 우리가 `x.send(bob)`를 실행했을 때 우리는 `x_ptr`이라는 새로운 객체를 반환했습니다. 이것은 텐서의 첫 번째 *포인터*입니다. 텐서에 대한 포인터는 실제로 데이터 자체를 보유하지 않습니다. 대신 다른 머신에 저장된 텐서(데이터 포함)에 대한 메타 데이터만 포함합니다. 이 텐서의 목적은 다른 머신에 텐서를 사용하여 함수를 계산하도록 지시하는 직관적인 API를 제공하는 것입니다. 포인터에 포함된 메타 데이터를 살펴보겠습니다.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:89072528493 -> bob:57522835638]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "메타 데이터를 확인하십시오!\n",
    "\n",
    "포인터와 관련된 두 가지 주요 속성이 있습니다.\n",
    "\n",
    "- `x_ptr.location : bob`, 포인터가 가리키는 위치에 대한 참조 \n",
    "- `x_ptr.id_at_location : <random integer>`, 위치에 텐서가 어디에 저장되어있는 지를 구별하는 id\n",
    "\n",
    "이는 다음과 같은 형식으로 출력됩니다 `<id_at_location>@<location>`\n",
    "\n",
    "다른 일반적인 속성들도 있습니다:\n",
    "- `x_ptr.id : <random integer>`, 포인터 텐서의 id로 무작위로 할당되었습니다. \n",
    "- `x_ptr.owner : \"me\"`, 포인터 텐서를 소유한 작업자입니다. 여기서는 \"me\"라는 로컬 작업자가 있습니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<VirtualWorker id:bob #objects:3>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_ptr.location"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<VirtualWorker id:bob #objects:3>"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bob"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bob == x_ptr.location"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "57522835638"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_ptr.id_at_location"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<VirtualWorker id:me #objects:0>"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_ptr.owner"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "여기서 포인터를 소유한 로컬 작업자가 VirtualWorker인 이유를 궁금해할 수도 있습니다. \n",
    "재미있는 사실은 Bob에 대한 VirtualWorker 객체가 있는 것처럼 항상 (기본값으로) 하나를 가지고 있다는 것입니다. 이 작업자는 우리가 `hook = sy.TorchHook()`을 호출할 때 자동으로 생성되므로 직접 만들 필요가 없습니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<VirtualWorker id:me #objects:0>"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "me = sy.local_worker\n",
    "me"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "me == x_ptr.owner"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "마지막으로 텐서에서 .send() 를 호출할 수 있는 것처럼 텐서의 포인터에서 .get() 을 호출하여 다시 가져올 수 있습니다!!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:89072528493 -> bob:57522835638]"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1, 2, 3, 4, 5])"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_ptr.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:65932880921 -> bob:51633454228]"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1, 1, 1, 1, 1])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y_ptr.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([ 2,  4,  6,  8, 10])"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{}"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "그리고 위에서 보면 알 수 있듯이... Bob은 더 이상 텐서가 없습니다!!! 우리 머신으로 다시 이동한 것입니다!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 섹션 1.2 - 텐서 포인터 사용하기\n",
    "\n",
    "이처럼 Bob으로부터 텐서를 보내고 받는 것은 좋지만 이것은 딥러닝이 아닙니다! 원격 텐서에서 텐서 _operations_을 수행할 수 있기를 원합니다. 다행히도 텐서 포인터는 이것을 매우 쉽게 만듭니다! 일반적인 텐서처럼 포인터를 사용할 수 있습니다!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5]).send(bob)\n",
    "y = torch.tensor([1,1,1,1,1]).send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [],
   "source": [
    "z = x + y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:7318090181 -> bob:90726746482]"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "그리고 짜잔!\n",
    "\n",
    "보이지 않는 곳에서 매우 강력한 무언가가 일어났습니다. x와 y가 로컬에서 덧셈을 계산하는 대신, 명령이 직렬화되어 Bob에게 전송되었으며, Bob은 계산을 수행하고 텐서 z를 만든 다음 z에 대한 포인터를 다시 우리에게 돌려주었습니다!\n",
    "\n",
    "포인터에서 .get() 을 호출하면 결과를 컴퓨터로 다시 받습니다!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([2, 3, 4, 5, 6])"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Torch 함수들\n",
    "\n",
    "이 API는 모든 Torch의 연산으로 확장되었습니다!!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:51688033442 -> bob:43473527353]"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:17431171314 -> bob:23525118265]"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:24000723394 -> bob:30451184684]"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z = torch.add(x,y)\n",
    "z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([2, 3, 4, 5, 6])"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 변수들 (backpropagation을 포함한!)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5.], requires_grad=True).send(bob)\n",
    "y = torch.tensor([1,1,1,1,1.], requires_grad=True).send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [],
   "source": [
    "z = (x + y).sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:18172817527 -> bob:45214999314]"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z.backward()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [],
   "source": [
    "x = x.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1., 2., 3., 4., 5.], requires_grad=True)"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1., 1., 1., 1., 1.])"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.grad"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "보시다시피 API는 매우 유연하며 *원격 데이터*에서 Torch에서 일반적으로 수행하는 거의 모든 작업을 수행할 수 있습니다. 이는 Federated Learning, Secure Multi-Party Computation 및 Differential Privacy와 같은 고급 개인 정보 보호 프로토콜의 토대를 마련합니다!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 축하합니다!!! - 이제 커뮤니티에 가입할 시간입니다!\n",
    "\n",
    "이 파이썬 노트북 튜토리얼을 완료한 것을 축하합니다! 이 튜토리얼에 만족하셨다면, 그리고 AI와 AI 공급망(데이터)의 사생활을 보존하고 탈중앙화된 소유권을 부여하는 움직임에 동참하고 싶다면 다음과 같이하십시오!\n",
    "\n",
    "### PySyft github의 Star버튼을 누르세요\n",
    "\n",
    "커뮤니티를 돕는 가장 쉬운 방법은 GitHub 리포지토리를 널리 알리는 것입니다! 이를 통해 우리가 만드는 멋진 도구에 대한 인식을 높일 수 있습니다.\n",
    "\n",
    "- [Star PySyft](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### 우리 Slack에 가입하세요!\n",
    "\n",
    "최신 소식을 받는 가장 좋은 방법은 커뮤니티에 가입하는 것입니다! [http://slack.openmined.org](http://slack.openmined.org)에서 양식을 작성하면 됩니다.\n",
    "\n",
    "### Code Project에 참여하십시오!\n",
    "\n",
    "커뮤니티에 기여하는 가장 좋은 방법은 code contributor가 되는 것입니다! 언제든지 PySyft GitHub 이슈 페이지로 이동하여 \"Projects\"를 필터링 할 수 있습니다. 그러면 참여할 수 있는 프로젝트에 대한 개요를 제공하는 모든 최상위 티켓이 표시됩니다. 프로젝트에 참여하고 싶지는 않지만 약간의 코딩을 원한다면 \"good first issue\"로 표시된 GitHub 문제를 검색하여 더 많은 \"일회용\" 미니 프로젝트를 찾을 수도 있습니다.\n",
    "\n",
    "- [PySyft Projects](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Good First Issue Tickets](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### 기부하기\n",
    "\n",
    "코드에 기여할 시간이 없지만 지원을 하고 싶다면 Open Collective의 후원자가 될 수도 있습니다. 모든 기부금은 웹 호스팅 및 해커톤 및 밋업과 같은 기타 커뮤니티 비용으로 들어갑니다!\n",
    "\n",
    "[OpenMined's Open Collective Page](https://opencollective.com/openmined)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false
    }
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.5"
  },
  "pycharm": {
   "stem_cell": {
    "cell_type": "raw",
    "metadata": {
     "collapsed": false
    },
    "source": []
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
